package com.arraymasterx.algorithm.matrix;

import com.arraymasterx.config.AlgorithmConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

@Component
public class MatrixMultiplication implements MatrixAlgorithm {

    private final AlgorithmConfig algorithmConfig;

    @Autowired
    public MatrixMultiplication(AlgorithmConfig algorithmConfig) {
        this.algorithmConfig = algorithmConfig;
    }

    @Override
    @Cacheable(value = "matrixProducts", key = "{#matrixA, #matrixB}")
    public int[][] execute(int[][] matrixA, int[][] matrixB) {
        validateMatrices(matrixA, matrixB);

        int rowsA = matrixA.length;
        int colsA = matrixA[0].length;
        int colsB = matrixB[0].length;

        int[][] result = new int[rowsA][colsB];

        if (rowsA > algorithmConfig.getMatrix().getParallelThreshold()) {
            // 并行计算
            parallelMultiply(matrixA, matrixB, result);
        } else {
            // 顺序计算
            sequentialMultiply(matrixA, matrixB, result);
        }

        return result;
    }

    private void sequentialMultiply(int[][] matrixA, int[][] matrixB, int[][] result) {
        int rowsA = matrixA.length;
        int colsA = matrixA[0].length;
        int colsB = matrixB[0].length;

        for (int i = 0; i < rowsA; i++) {
            for (int j = 0; j < colsB; j++) {
                for (int k = 0; k < colsA; k++) {
                    result[i][j] += matrixA[i][k] * matrixB[k][j];
                }
            }
        }
    }

    private void parallelMultiply(int[][] matrixA, int[][] matrixB, int[][] result) {
        int rowsA = matrixA.length;
        int colsB = matrixB[0].length;
        int colsA = matrixA[0].length;

        java.util.stream.IntStream.range(0, rowsA).parallel().forEach(i -> {
            for (int j = 0; j < colsB; j++) {
                for (int k = 0; k < colsA; k++) {
                    result[i][j] += matrixA[i][k] * matrixB[k][j];
                }
            }
        });
    }

    private void validateMatrices(int[][] matrixA, int[][] matrixB) {
        MatrixUtils.validateMatrix(matrixA, algorithmConfig);
        MatrixUtils.validateMatrix(matrixB, algorithmConfig);

        if (matrixA[0].length != matrixB.length) {
            throw new IllegalArgumentException(
                    "Number of columns in first matrix must equal number of rows in second matrix");
        }
    }
}